import * as React from "react";
import { useEffect, useRef, useState } from "react";

export interface HTMLMediaProps
  extends React.AudioHTMLAttributes<any>,
    React.VideoHTMLAttributes<any> {
  src: string;
}

export interface PlayState {
  playState: boolean;
}

export interface HTMLMediaState {
  buffered: Array<string>;
  duration: number;
  paused: boolean;
  muted: boolean;
  time: number;
  volume: number;
}

export interface HTMLMediaControls {
  play: () => Promise<void> | void;
  pause: () => void;
  mute: () => void;
  unmute: () => void;
  volume: (volume: number) => void;
  seek: (time: number) => void;
}

const createHTMLMediaHook = (tag: "audio" | "video") => {
  const Hook = (
    elOrProps: HTMLMediaProps | React.ReactElement<HTMLMediaProps>
  ): [
    React.ReactElement<HTMLMediaProps>,
    HTMLMediaState,
    HTMLMediaControls,
    { current: HTMLAudioElement | null },
    PlayState,
    number,
    number
  ] => {
    let element: React.ReactElement | undefined;
    let props: HTMLMediaProps;

    if (React.isValidElement(elOrProps)) {
      element = elOrProps;
      props = element.props;
    } else {
      props = elOrProps as HTMLMediaProps;
    }

    const [playState, setPlayState] = useState<PlayState>({
      playState: false,
    });

    const [duration, setDuration] = useState<number>(0);

    const [state, setState] = useState<HTMLMediaState>({
      buffered: [],
      time: 0,
      duration: 0,
      paused: true,
      muted: false,
      volume: 1,
    });

    const [currentTime, setCurrentTime] = useState<number>(0);

    const ref = useRef<HTMLAudioElement | null>(null);

    const wrapEvent = (userEvent: any, proxyEvent?: any) => {
      return (event: React.BaseSyntheticEvent) => {
        try {
          proxyEvent && proxyEvent(event);
        } finally {
          userEvent && userEvent(event);
        }
      };
    };

    const onPlay = () => {
      // console.log("触发播放");
      setState({
        buffered: [],
        time: 0,
        duration: 0,
        paused: false,
        muted: false,
        volume: 1,
      });
    };

    const onPause = () => {
      // console.log("触发暂停");
      setState({
        buffered: [],
        time: 0,
        duration: 0,
        paused: true,
        muted: false,
        volume: 1,
      });
    };

    const onCanPlay = () => {
      setPlayState({
        playState: true,
      });
    };

    const onVolumeChange = () => {
      const el = ref.current;
      if (!el) {
        return;
      }
      // console.log(el, "onVolumeChange");
      // setState({
      //   muted: el.muted,
      //   volume: el.volume,
      // });
    };

    const onDurationChange = () => {
      const el = ref.current;
      if (!el) {
        return;
      }
      const { duration } = el;
      setDuration(duration);
    };

    const onTimeUpdate = () => {
      const el = ref.current;
      if (!el) {
        return;
      }
      setCurrentTime(el.currentTime);
    };

    const onProgress = () => {
      const el = ref.current;
      if (!el) {
        return;
      }
      // console.log(el, "onProgress");
      // setState({
      //   buffered: parseTimeRanges(el.buffered),
      // });
    };

    if (element) {
      element = React.cloneElement(element, {
        controls: false,
        ...props,
        ref,
        onPlay: wrapEvent(props.onPlay, onPlay),
        onPause: wrapEvent(props.onPause, onPause),
        onVolumeChange: wrapEvent(props.onVolumeChange, onVolumeChange),
        onDurationChange: wrapEvent(props.onDurationChange, onDurationChange),
        onTimeUpdate: wrapEvent(props.onTimeUpdate, onTimeUpdate),
        onProgress: wrapEvent(props.onProgress, onProgress),
      });
    } else {
      element = React.createElement(tag, {
        controls: false,
        ...props,
        ref,
        onCanPlay: wrapEvent(props.onCanPlay, onCanPlay),
        onPlay: wrapEvent(props.onPlay, onPlay),
        onPause: wrapEvent(props.onPause, onPause),
        onVolumeChange: wrapEvent(props.onVolumeChange, onVolumeChange),
        onDurationChange: wrapEvent(props.onDurationChange, onDurationChange),
        onTimeUpdate: wrapEvent(props.onTimeUpdate, onTimeUpdate),
        onProgress: wrapEvent(props.onProgress, onProgress),
      });
    }

    let lockPlay = false;

    const controls = {
      play: () => {
        const el = ref.current;
        if (!el) {
          return undefined;
        }
        if (!lockPlay) {
          const promise = el.play();

          const isPromise = typeof promise === "object";

          if (isPromise) {
            lockPlay = true;
            const resetLock = () => {
              lockPlay = false;
            };
            promise.then(resetLock, resetLock);
          }

          return promise;
        }

        return undefined;
      },

      pause: () => {
        const el = ref.current;
        if (el && !lockPlay) {
          return el.pause();
        }
      },
      seek: (time: number) => {
        const el = ref.current;
        if (!el || state.duration === undefined) {
          return;
        }
        time = Math.min(state.duration, Math.max(0, time));
        el.currentTime = time;
      },
      volume: (volume: number) => {
        const el = ref.current;
        if (!el) {
          return;
        }
        volume = Math.min(1, Math.max(0, volume));
        el.volume = volume;
        setState({ volume });
      },
      mute: () => {
        const el = ref.current;
        if (!el) {
          return;
        }
        el.muted = true;
      },
      unmute: () => {
        const el = ref.current;
        if (!el) {
          return;
        }
        el.muted = false;
      },
    };

    useEffect(() => {
      const el = ref.current;
      if (!el) {
        if (process.env.NODE_ENV !== "production") {
          if (tag === "audio") {
            // console.error("123");
          } else if (tag === "video") {
            // console.error("456");
          }
        }
        return;
      }

      // setState({
      //   volume: el.volume,
      //   muted: el.muted,
      //   paused: el.paused,
      // });

      if (props.autoPlay && el.paused) {
        controls.play();
      }
    }, [props.src]);

    return [element, state, controls, ref, playState, duration, currentTime];
  };

  return Hook;
};

export default createHTMLMediaHook;
